package cn.stimd.spring.beans.factory.annotation;

import cn.stimd.spring.beans.BeanUtils;
import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.factory.BeanFactory;
import cn.stimd.spring.beans.factory.BeanFactoryAware;
import cn.stimd.spring.beans.factory.config.ConfigurableBeanFactory;
import cn.stimd.spring.beans.factory.config.DependencyDescriptor;
import cn.stimd.spring.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, PriorityOrdered {
    private Log logger = LogFactory.getLog(getClass());
    private String requiredParameterName = "required";
    private boolean requiredParameterValue = true;
    private int order = Ordered.LOWEST_PRECEDENCE - 2;

    private ConfigurableBeanFactory beanFactory;
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
    private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
    //与Class关联的构造器缓存
    private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);


    public AutowiredAnnotationBeanPostProcessor(){
        this.autowiredAnnotationTypes.add(Autowired.class);
        this.autowiredAnnotationTypes.add(Value.class);

        //兼容@Inject注解
        try {
            this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.inject.Inject", getClass().getClassLoader()));
        }catch (ClassNotFoundException ex) {
            // ignore
        }
    }


    @Override
    public void postProcessPropertyValues(Object bean, String beanName) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass());
        try {
            metadata.inject(bean, beanName);
        } catch (Throwable t) {
            throw new RuntimeException("依赖注入失败, bean: " + bean.getClass().getName() + ", beanName: " + beanName, t);
        }
    }


    //寻找自动装配的元数据
    private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz) {
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        //如果缓存中没有InjectionMetadata，则构建新的实例
        if(metadata == null){
            metadata = buildAutowiringMetadata(clazz);
            this.injectionMetadataCache.put(cacheKey, metadata);
        }
        return metadata;
    }


    //构建自动装配的元数据
    public InjectionMetadata buildAutowiringMetadata(final Class<?> clazz){
        LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
        Class<?> targetClass = clazz;

        //遍历Bean及其父类上的字段和方法
        do {
            //检查字段上是否有@AutoWired、@Inject等注解
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                AnnotationAttributes attrs = findAutowiredAnnotation(field);
                if(attrs != null){
                    boolean required = determineRequiredStatus(attrs);
                    elements.add(new AutowiredFieldElement(field, required));
                }
            });

            //检查方法上是否有@AutoWired、@Inject等注解
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }

                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                if(ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))){
                    boolean required = determineRequiredStatus(ann);
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    elements.add(new AutowiredMethodElement(method, required, pd));
                }
            });

            targetClass = targetClass.getSuperclass();
        }
        while(targetClass != null & targetClass != Object.class);
        return new InjectionMetadata(clazz, elements);
    }


    //判断注解是否是必须的
    //@Autowired有required属性，因此要判断required是否为true。@Inject没有required属性，默认就是必须的
    private boolean determineRequiredStatus(AnnotationAttributes attrs) {
        return (!attrs.containsKey(this.requiredParameterName) ||
                this.requiredParameterValue == attrs.getBoolean(this.requiredParameterName));
    }


    //获取可访问对象（包括属性、方法、构造器）上的注解
    private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
        if (ao.getAnnotations().length > 0) {
            for(Class<? extends Annotation> type : this.autowiredAnnotationTypes){
                AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
                if (attributes != null) {
                    return attributes;
                }
            }
        }
        return null;
    }


    /**
     * 找出符合条件的候选构造器，需满足以下几个原则
     * 1) 使用注解声明，且required为true，那么chosenCtors只有一个
     * 2) 使用@Autowired(required=false)声明，候选的构造器可以有多个。如果存在无参构造器，也加入候选列表
     * 3) 只有一个有参构造器，无论有没有使用注解声明
     */
    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) {
        //1. 尝试从缓存中查找
        Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
        if (candidateConstructors == null) {
            synchronized (this.candidateConstructorsCache) {
                candidateConstructors = this.candidateConstructorsCache.get(beanClass);
                if (candidateConstructors == null) {
                    Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
                    List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
                    Constructor<?> requiredConstructor = null;
                    Constructor<?> defaultConstructor = null;

                    //2. 遍历构造器集合
                    for (Constructor<?> candidate : rawCandidates) {
                        //2.1 获取构造器的注解信息
                        AnnotationAttributes ann = findAutowiredAnnotation(candidate);
                        if (ann == null) {
                            //Bean可能是Cglib方式生成的，尝试获取原始类的构造器注解
                            Class<?> userClass = ClassUtils.getUserClass(beanClass);
                            if (userClass != beanClass) {
                                try {
                                    Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes());
                                    ann = findAutowiredAnnotation(superCtor);
                                }
                                catch (NoSuchMethodException ex) {
                                    // ignore
                                }
                            }
                        }

                        //2.2 如果存在注解，则将构造器加入候选列表
                        if (ann != null) {
                            //确保标记为自动装配且必须的构造器只有一个
                            if(requiredConstructor != null){
                                throw new BeansException("创建Bean[" + beanName + "]失败: 构造器[" + candidate +
                                        "]不能被标记为自动注入，已经存在被标记为自动注入的构造器: " + requiredConstructor);
                            }

                            //判断required是否为true
                            boolean required = determineRequiredStatus(ann);
                            if(required){
                                requiredConstructor = candidate;
                            }
                            candidates.add(candidate);
                        }
                        //2.3 如果存在无参构造器，先缓存起来，作为可选构造器的补充
                        else if (candidate.getParameterTypes().length == 0) {
                            defaultConstructor = candidate;
                        }
                    }

                    //3. 过滤符合条件的构造器
                    if (!candidates.isEmpty()) {
                        //如果不存在required属性为true的构造器，且无参构造器存在，则加入到临时集合
                        if (requiredConstructor == null && defaultConstructor != null) {
                            candidates.add(defaultConstructor);
                        }
                        candidateConstructors = candidates.toArray(new Constructor<?>[0]);
                    }
                    //只有一个构造器，且有参数
                    else if (rawCandidates.length == 1 && rawCandidates[0].getParameterTypes().length > 0) {
                        candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
                    }
                    //其余情况不予考虑，如果进入这个分支，最终返回null
                    else {
                        candidateConstructors = new Constructor<?>[0];
                    }
                    this.candidateConstructorsCache.put(beanClass, candidateConstructors);
                }
            }
        }
        return (candidateConstructors.length > 0 ? candidateConstructors : null);
    }


    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }


    /**
     * 内部类，对字段进行自动装配
     */
    private class AutowiredFieldElement extends InjectionMetadata.InjectedElement{
        private final boolean required;

        public AutowiredFieldElement(Field field, boolean required) {
            super(field, null);
            this.required = required;
        }


        /**
         * 对依赖进行解析，并注入到字段上
         */
        @Override
        protected void inject(Object bean, String beanName) throws Throwable{
            Field field = (Field) this.member;
            Object value;

            //创建依赖描述符
            DependencyDescriptor desc= new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            logger.info("[Beans] [依赖注入] --> 目标对象: " + beanName + ", 字段名: " + field.getName());

            //对依赖进行解析，如果Bean不存在则创建并加入到IOC容器中
            value = beanFactory.resolveDependency(desc, beanName);

            //使用反射为字段赋值
            if(value != null){
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }
        }
    }


    /**
     * 内部类，对setter方法进行自动装配
     */
    private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
        private final boolean required;
        private volatile boolean cached = false;
        private volatile Object[] cachedMethodArguments;

        public AutowiredMethodElement(Method method, boolean required, PropertyDescriptor pd) {
            super(method, pd);
            this.required = required;
        }

        @Override
        protected void inject(Object bean, String beanName) throws Throwable {
            Method method = (Method) this.member;
            Class<?>[] paramTypes = method.getParameterTypes();
            Object[] arguments = new Object[paramTypes.length];
            DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];

            for (int i = 0; i < arguments.length; i++) {
                MethodParameter methodParam = new MethodParameter(method, i);
                DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
                currDesc.setContainingClass(bean.getClass());
                descriptors[i] = currDesc;

                logger.info("[Beans] [依赖注入] --> 目标对象: " + beanName + ", 方法名: " + method.getName());
                Object arg = beanFactory.resolveDependency(currDesc, beanName);
                //如果依赖项不存在，且非required，直接跳出循环
                if (arg == null && !this.required) {
                    arguments = null;
                    break;
                }
                arguments[i] = arg;
            }

            if(arguments != null){
                ReflectionUtils.makeAccessible(method);
                method.invoke(bean, arguments);
            }
        }
    }
}
